package org.dbstar.scheduler.utils;

import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.FunctionMapper;

import org.apache.commons.el.Expression;
import org.apache.commons.el.ExpressionString;
import org.apache.commons.el.parser.ELParser;
import org.apache.commons.el.parser.ParseException;
import org.apache.commons.lang.StringUtils;
import org.dbstar.scheduler.beans.JobConf;
import org.dbstar.scheduler.beans.ParameterDefine;
import org.dbstar.scheduler.job.JobStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Job工具类
 * 
 * @author 代波
 * @since 1.0.0
 * @version 1.0.0
 * @date 2012-5-7
 */
@SuppressWarnings("deprecation")
public class JobUtils {
	private static final Logger LOGGER = LoggerFactory.getLogger(JobUtils.class);

	private static final MyFunctionMapper FUNCTION_MAPPER = new MyFunctionMapper();
	private static final org.apache.commons.el.Logger EL_LOGGER = new org.apache.commons.el.Logger(System.out);

	private static final Set<JobStatus> finishedStatus = new HashSet<JobStatus>();
	static {
		finishedStatus.add(JobStatus.kill);
		finishedStatus.add(JobStatus.error);
		finishedStatus.add(JobStatus.cancel);
		finishedStatus.add(JobStatus.finish);
	}

	public static Map<String, String> getJobParameters(JobConf conf, Map<String, String> parameters,
			boolean setDefineValue) throws IllegalArgumentException {
		if (conf.getParameterSize() > 0) {
			Map<String, String> p = new HashMap<String, String>(conf.getParameterSize());
			long millis = System.currentTimeMillis();
			for (ParameterDefine pd : conf.getParameters()) {
				String name = pd.getName();
				String val = parameters == null ? null : parameters.get(name);
				if (StringUtils.isBlank(val)) {
					String dv = pd.getDefineValue(millis);
					if (dv == null) {
						throw new IllegalArgumentException("参数 " + name + " 必须设定一个有效值！");
					} else {
						if (setDefineValue) {
							p.put(name, dv);
						}
					}
				} else {
					p.put(name, val.trim());
				}
			}
			return p;
		} else {
			return null;
		}
	}

	public static boolean isFinished(JobStatus status) {
		return finishedStatus.contains(status);
	}

	public static String formatByParameter(String val, Map<String, String> parameters) {
		if (parameters != null && !parameters.isEmpty()) {
			for (Entry<String, String> entry : parameters.entrySet()) {
				val = StringUtils.replace(val, entry.getKey(), entry.getValue());
			}
		}
		String fns[] = StringUtils.substringsBetween(val, "!{", "}");
		if (fns != null) {
			for (String fn : fns) {
				String v = "";
				String p[] = StringUtils.splitPreserveAllTokens(fn, '|');
				if (p.length > 0) {
					try {
						if ("eq".equals(p[0]) && p.length == 4) {
							// eq|inputVal|compareVal|valIfEquals
							v = p[1].equals(p[2]) ? p[3] : p[1];
						} else if ("dc".equals(p[0]) && p.length == 4) {
							// dc|inputVal|inputFormat|convertFormat
							v = CalendarUtils.convert(p[1], p[2], p[3]);
						} else if ("dr".equals(p[0]) && p.length == 5) {
							// dr|inputVal|format|field|amount
							v = CalendarUtils.format(
									CalendarUtils.roll(CalendarUtils.date(p[1], p[2]), Integer.parseInt(p[3]),
											Integer.parseInt(p[4])), p[2]);
						}
					} catch (Throwable e) {
						LOGGER.error("按参数格式化时发生异常：" + val, e);
						v = "";
					}
				}
				val = StringUtils.replace(val, "!{" + fn + "}", v);
			}
		}
		return val;
	}

	public static Map<String, String> formatSubJobParameters(Map<String, String> subParameters,
			Map<String, String> parentParameters) {
		if (parentParameters == null || parentParameters.isEmpty()) {
			return subParameters;
		} else if (subParameters == null || subParameters.isEmpty()) {
			return null;
		} else {
			Map<String, String> parameters = new HashMap<String, String>();
			for (String name : subParameters.keySet()) {
				String val = subParameters.get(name);
				parameters.put(name, JobUtils.formatByParameter(val, parentParameters));
			}
			return parameters;
		}
	}

	public static Object parseExpression(String expression) throws IllegalArgumentException {
		if (expression == null) {
			return null;
		}

		ELParser parser = new ELParser(new StringReader("${" + expression + "}"));
		Object o;
		try {
			o = parser.ExpressionString();
		} catch (ParseException e) {
			throw new IllegalArgumentException("解析表达式失败：" + expression, e);
		}

		if (o == null) {
			return null;
		} else if (ExpressionString.class.isInstance(o)) {
			try {
				return ((ExpressionString) o).evaluate(null, FUNCTION_MAPPER, EL_LOGGER);
			} catch (ELException e) {
				throw new IllegalArgumentException("解析表达式失败：" + expression, e);
			}
		} else if (Expression.class.isInstance(o)) {
			try {
				return ((Expression) o).evaluate(null, FUNCTION_MAPPER, EL_LOGGER);
			} catch (ELException e) {
				throw new IllegalArgumentException("解析表达式失败：" + expression, e);
			}
		} else {
			return o;
		}
	}

	public static boolean matchCondition(String condition, Map<String, String> parameters) {
		if (condition != null) {
			// 根据条件字符串来解析结果
			Object r;
			try {
				r = JobUtils.parseExpression(JobUtils.formatByParameter(condition, parameters));
			} catch (Throwable e) {
				LOGGER.error("解析依赖匹配条件失败：" + condition, e);
				return false;
			}
			if (r != null && Boolean.class.isInstance(r)) {
				return ((Boolean) r).booleanValue();
			} else {
				LOGGER.warn("解析依赖匹配条件失败：{}, 其结果不为布尔型：{}", condition, r);
				return false;
			}
		} else {
			return true;
		}
	}

	public static class MyFunctionMapper implements FunctionMapper {
		private final static Map<String, Method> methodMap = new HashMap<String, Method>();

		private MyFunctionMapper() {
			super();
			try {
				addMethod("c", CalendarUtils.class, "date", String.class, String.class);
				addMethod("c", CalendarUtils.class, "format", Calendar.class, String.class);
				addMethod("c", CalendarUtils.class, "convert", String.class, String.class, String.class);
				addMethod("c", CalendarUtils.class, "field", Calendar.class, int.class);
				addMethod("c", CalendarUtils.class, "roll", Calendar.class, int.class, int.class);
				addMethod("c", CalendarUtils.class, "dayOfWeek", Calendar.class);
				addMethod("c", CalendarUtils.class, "dayOfYear", Calendar.class);
			} catch (Throwable e) {
				LOGGER.error("获取方法失败。", e);
			}
		}

		private void addMethod(String prefix, Class<?> clazz, String name, Class<?>... parameterTypes)
				throws SecurityException, NoSuchMethodException {
			methodMap.put((prefix == null ? "" : prefix + ':') + name, clazz.getMethod(name, parameterTypes));
		}

		@Override
		public Method resolveFunction(String prefix, String localName) {
			return methodMap.get((prefix == null ? "" : prefix + ':') + localName);
		}
	}
}
